/**
 * @file spi_emu.c
 * @author zhaitao (zhaitao.as@outlook.com)
 * @brief emulattion spi
 * @version 0.1
 * @date 2020-07-22
 *
 * @copyright zhaitao.as@outlook.com (c) 2020
 *
 */

#include "spi_emu.h"

#define HIGH (1)
#define LOW (0)

/**
 * @brief initial a emulation spi
 * 
 * @param dev emulation spi instance
 */
void seInitial(struct SpiEmu *dev)
{
	if (dev->bits != 0) {
		dev->readBits = dev->bits;
		dev->writeBits = dev->bits;
	}

	dev->cs(1);

	if (dev->CPOL) {
		dev->sck(1);
	} else {
		dev->sck(0);
	}

	if (dev->CPHA) {
		dev->leadingEdge = leadingEdgeCPHA1;
		dev->trailingEdge = trailingEdgeCPHA1;
	} else {
		dev->leadingEdge = leadingEdgeCPHA0;
		dev->trailingEdge = trailingEdgeCPHA0;
	}
}

/**
 * @brief deinitial a emulation spi
 * 
 * @param dev emulation spi instance
 */
void seDeInitial(struct SpiEmu *dev)
{
	free(dev);
}

/**
 * @brief transfer a byte
 * 
 * @param dev emulation spi instance
 * @param byte_out byte to be send
 * @return uint32_t byte recived
 */
inline uint32_t seTransfer(struct SpiEmu *dev, uint32_t byte_out)
{
	uint32_t byte_in = 0;
	uint32_t bit = 1 << dev->bits;

	for (; bit; bit >>= 1) {
		/* Shift-out a bit to the MOSI line */
		dev->mosi((byte_out & bit) ? HIGH : LOW);

		dev->leadingEdge(dev);

		/* Shift-in a bit from the MISO line */
		if (dev->miso() == HIGH)
			byte_in |= bit;

		dev->trailingEdge(dev);
	}

	return byte_in;
}

/**
 * 
 * private function defines
 * 
 */

/**
 * @brief 
 * 
 * @param dev 
 */
void leadingEdgeCPHA0(struct SpiEmu *dev)
{
	/* Delay for at least the peer's setup time */
	dev->delayHalfClock();
	/* Pull the clock line high */
	dev->sck(!dev->CPOL);
}

void leadingEdgeCPHA1(struct SpiEmu *dev)
{
	/* Pull the clock line high */
	dev->sck(!dev->CPOL);
	/* Delay for at least the peer's setup time */
	dev->delayHalfClock();
}

void trailingEdgeCPHA0(struct SpiEmu *dev)
{
	/* Delay for at least the peer's setup time */
	dev->delayHalfClock();
	/* Pull the clock line high */
	dev->sck(dev->CPOL);
}

void trailingEdgeCPHA1(struct SpiEmu *dev)
{
	/* Pull the clock line high */
	dev->sck(dev->CPOL);
	/* Delay for at least the peer's setup time */
	dev->delayHalfClock();
}
